#!/usr/bin/env python3
"""
GitLab组织项目列表获取工具
将GitLab API调用转换为Python代码并生成DataFrame
"""

import json
import os
import random
from typing import Dict, List, Optional
from urllib.parse import quote_plus

import pandas as pd
import requests
from loguru import logger


class GitLabReposFetcher:
    """GitLab仓库信息获取器"""

    def __init__(self, access_token: str, gitlab_url: str = "http://gitlab.waterism.com:8888"):
        """
        初始化GitLab API客户端
        
        Args:
            access_token: GitLab访问令牌
            gitlab_url: GitLab实例URL
        """
        self.access_token = access_token
        self.gitlab_url = gitlab_url.rstrip('/')
        self.base_url = f"{self.gitlab_url}/api/v4"
        self.session = requests.Session()

        # 设置请求头
        self.session.headers.update({
            'User-Agent': 'GitLab-Python-Client/1.0',
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            'PRIVATE-TOKEN': self.access_token
        })

    def get_group_projects(self, group_id: str, **kwargs) -> List[Dict]:
        """
        获取群组的项目列表
        
        Args:
            group_id: 群组ID或路径
            **kwargs: 其他查询参数
                - visibility: 可见性 (public, internal, private)
                - order_by: 排序字段 (id, name, path, created_at, updated_at, last_activity_at)
                - sort: 排序方向 (asc, desc)
                - search: 搜索关键词
                - simple: 简化输出 (true, false)
                - per_page: 每页数量 (1-100, 默认20)
                - page: 页码 (默认1)
        
        Returns:
            项目列表
        """
        # 对群组ID进行URL编码
        encoded_group_id = quote_plus(str(group_id))
        url = f"{self.base_url}/groups/{encoded_group_id}/projects"

        # 构建查询参数
        params = {
            'visibility': kwargs.get('visibility', 'private'),
            'order_by': kwargs.get('order_by', 'last_activity_at'),
            'sort': kwargs.get('sort', 'desc'),
            'per_page': kwargs.get('per_page', 20),
            'page': kwargs.get('page', 1)
        }

        # 添加可选参数
        optional_params = ['search', 'simple']
        for param in optional_params:
            if param in kwargs and kwargs[param] is not None:
                params[param] = kwargs[param]

        try:
            response = self.session.get(url, params=params)
            response.raise_for_status()  # 抛出HTTP错误
            return response.json()
        except requests.exceptions.RequestException as e:
            logger.error(f"请求失败: {e}")
            if hasattr(e, 'response') and e.response is not None:
                logger.error(f"响应状态码: {e.response.status_code}")
                logger.error(f"响应内容: {e.response.text}")
            return []
        except json.JSONDecodeError as e:
            logger.error(f"JSON解析失败: {e}")
            return []

    def get_all_projects(self, **kwargs) -> List[Dict]:
        """
        获取所有可访问的项目
        
        Args:
            **kwargs: 查询参数
                - visibility: 可见性 (public, internal, private)
                - order_by: 排序字段
                - sort: 排序方向
                - search: 搜索关键词
                - per_page: 每页数量
        
        Returns:
            项目列表
        """
        url = f"{self.base_url}/projects"

        # 构建查询参数
        params = {
            'membership': True,  # 只获取用户是成员的项目
            'order_by': kwargs.get('order_by', 'last_activity_at'),
            'sort': kwargs.get('sort', 'desc'),
            'per_page': kwargs.get('per_page', 20)
        }

        # 添加可选参数
        optional_params = ['visibility', 'search']
        for param in optional_params:
            if param in kwargs and kwargs[param] is not None:
                params[param] = kwargs[param]

        all_projects = []
        page = 1

        while True:
            params['page'] = page

            try:
                response = self.session.get(url, params=params)
                response.raise_for_status()
                projects = response.json()

                if not projects:
                    break

                all_projects.extend(projects)

                # 检查是否还有更多页面
                if len(projects) < params['per_page']:
                    break

                page += 1

            except requests.exceptions.RequestException as e:
                logger.error(f"获取第 {page} 页项目时失败: {e}")
                break

        logger.info(f"总共获取到 {len(all_projects)} 个项目")
        return all_projects

    def projects_to_dataframe(self, projects_data: List[Dict]) -> pd.DataFrame:
        """
        将项目数据转换为DataFrame
        
        Args:
            projects_data: 项目数据列表
        
        Returns:
            包含项目信息的DataFrame
        """
        if not projects_data:
            return pd.DataFrame()

        # 提取关键字段
        processed_data = []
        for project in projects_data:
            processed_project = {
                'id': project.get('id'),
                'name': project.get('name'),
                'path': project.get('path'),
                'path_with_namespace': project.get('path_with_namespace'),
                'description': project.get('description', ''),
                'default_branch': project.get('default_branch', 'main'),
                'visibility': project.get('visibility', 'private'),
                'web_url': project.get('web_url', ''),
                'ssh_url_to_repo': project.get('ssh_url_to_repo', ''),
                'http_url_to_repo': project.get('http_url_to_repo', ''),
                'created_at': project.get('created_at'),
                'updated_at': project.get('updated_at'),
                'last_activity_at': project.get('last_activity_at'),
                'star_count': project.get('star_count', 0),
                'forks_count': project.get('forks_count', 0),
                'issues_enabled': project.get('issues_enabled', False),
                'merge_requests_enabled': project.get('merge_requests_enabled', False),
                'wiki_enabled': project.get('wiki_enabled', False),
                'archived': project.get('archived', False),
                'empty_repo': project.get('empty_repo', False),
                'namespace_name': project.get('namespace', {}).get('name', ''),
                'namespace_path': project.get('namespace', {}).get('path', ''),
                'owner_name': project.get('owner', {}).get('name', '') if project.get('owner') else '',
                'owner_username': project.get('owner', {}).get('username', '') if project.get('owner') else ''
            }
            processed_data.append(processed_project)

        df = pd.DataFrame(processed_data)

        # 转换日期列
        date_columns = ['created_at', 'updated_at', 'last_activity_at']
        for col in date_columns:
            if col in df.columns:
                df[col] = pd.to_datetime(df[col], errors='coerce')

        return df

    def get_all_projects_dataframe(self, group_id: Optional[str] = None, **kwargs) -> pd.DataFrame:
        """
        获取所有项目并返回DataFrame
        
        Args:
            group_id: 可选的群组ID，如果提供则只获取该群组的项目
            **kwargs: 其他查询参数
        
        Returns:
            包含所有项目信息的DataFrame
        """
        logger.info("开始获取GitLab项目列表...")

        if group_id:
            logger.info(f"获取群组 {group_id} 的项目...")
            projects_data = self.get_group_projects(group_id, **kwargs)
        else:
            logger.info("获取所有可访问的项目...")
            projects_data = self.get_all_projects(**kwargs)

        if not projects_data:
            logger.warning("没有获取到任何项目")
            return pd.DataFrame()

        logger.info(f"成功获取 {len(projects_data)} 个项目")

        # 转换为DataFrame
        df = self.projects_to_dataframe(projects_data)

        # 显示基本统计信息
        if not df.empty:
            logger.info(f"项目统计:")
            logger.info(f"  - 总项目数: {len(df)}")
            logger.info(f"  - 公开项目: {len(df[df['visibility'] == 'public'])}")
            logger.info(f"  - 私有项目: {len(df[df['visibility'] == 'private'])}")
            logger.info(f"  - 已归档项目: {len(df[df['archived'] == True])}")
            logger.info(f"  - 空仓库: {len(df[df['empty_repo'] == True])}")

        return df

    def get_project_info(self, project_id: str) -> Dict:
        """
        获取单个项目的详细信息
        
        Args:
            project_id: 项目ID或路径
        
        Returns:
            项目详细信息
        """
        # 对项目ID进行URL编码
        encoded_project_id = quote_plus(str(project_id))
        url = f"{self.base_url}/projects/{encoded_project_id}"

        try:
            response = self.session.get(url)
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException as e:
            logger.error(f"获取项目 {project_id} 信息失败: {e}")
            return {}


def get_random_gitlab_token():
    """
    从环境变量中随机获取一个GitLab访问令牌
    支持多个令牌以避免API限制
    
    Returns:
        随机选择的访问令牌
    """
    tokens_str = os.getenv('GITLAB_ACCESS_TOKEN', '')
    if not tokens_str:
        raise ValueError("请设置环境变量 GITLAB_ACCESS_TOKEN")

    tokens = [token.strip() for token in tokens_str.split(',') if token.strip()]
    if not tokens:
        raise ValueError("GITLAB_ACCESS_TOKEN 环境变量为空")

    return random.choice(tokens)


def main():
    """
    主函数 - 获取GitLab项目列表并保存为CSV
    """
    try:
        # 从环境变量获取配置
        gitlab_url = os.getenv('GITLAB_URL', 'http://gitlab.waterism.com:8888')
        access_token = get_random_gitlab_token()

        # 创建获取器
        fetcher = GitLabReposFetcher(access_token, gitlab_url)

        # 获取所有项目
        projects_df = fetcher.get_all_projects_dataframe()

        if projects_df.empty:
            logger.warning("没有获取到任何项目数据")
            return None

        # 保存到CSV文件
        output_file = 'gitlab_projects.csv'
        projects_df.to_csv(output_file, index=False, encoding='utf-8')
        logger.info(f"项目列表已保存到 {output_file}")

        # 显示前几行数据
        logger.info("\n=== GitLab项目列表预览 ===")
        logger.info(projects_df[['id', 'name', 'path_with_namespace', 'visibility', 'last_activity_at']].head(10))

        # 显示统计信息
        logger.info("\n=== 统计信息 ===")
        logger.info(f"总项目数: {len(projects_df)}")
        logger.info(f"可见性分布:")
        logger.info(projects_df['visibility'].value_counts())

        return projects_df

    except Exception as e:
        logger.error(f"获取GitLab项目列表失败: {e}")
        return None


if __name__ == "__main__":
    projects_df = main()
